﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace InnovTech.Util.Workflow
{
    public class Workflow
    {
        public Workflow(IEnumerable<IWorkStep> steps) : this()
        {
            steps.ToList().ForEach(step => AddStep(step));
        }

        public Workflow()
        {
            _steps = new Dictionary<object, IWorkStep>();
        }

        Dictionary<object, IWorkStep> _steps;
        IWorkStep Current { get; set; }

        public IWorkStep StartPoint { get; set; }



        public void AddStep<TInput, TResult>(Func<TInput, TResult> logic)
        {
            AddStep(new WorkStep<TInput, TResult>(logic));
        }

        //public void AddDecision(Func<bool> decision)
        //{
        //    AddStep(new Decision(decision));
        //}

        //public void AddDecisionTrueStep(Decision decision, IWorkStep trueStep)
        //{
        //    decision.TrueStep = trueStep;
        //    AddStep(trueStep);
        //}

        //public void AddDecisionFalseStep(Decision decision, IWorkStep falseStep)
        //{
        //    decision.FalseStep = falseStep;
        //    AddStep(falseStep);
        //}

        public void AddStep(Action logic)
        {
            AddStep(new WorkStep(logic));
        }

        public void AddStep(IWorkStep step)
        {
            if (!_steps.ContainsKey(step.Id))
            {
                _steps.Add(step.Id, step);
                if (_steps.Count == 1)
                    StartPoint = step;
            }
        }

        public void AddNextStep(IWorkStep parent, IWorkStep child)
        {
            parent.NextStep = child;
            if (!Contains(parent))
                AddStep(parent);
            if (!Contains(child))
                AddStep(child);
        }

        private bool Contains(IWorkStep step)
        {
            return _steps.Keys.Contains(step.Id);
        }


        public void Run()
        {
            if (Starting != null)
            {
                CancelEventArgs cArgs = new CancelEventArgs();
                Starting(this, cArgs);
                if (cArgs.Cancel)
                    return;
            }
            Current = StartPoint;
            object result = null;
            while (Current != null)
            {
                Current.SetInput(result);
                Current.Run();

                if (StepCompleted != null)
                {
                    StepCompletedEventArgs sArgs = new StepCompletedEventArgs(Current);
                    StepCompleted(this, sArgs);
                    if (sArgs.Cancel)
                        return;
                }
                result = Current.Result;
                Current = Current.NextStep;
                
            }
            if (Completed != null)
                Completed(this, EventArgs.Empty);
        }

        public event EventHandler<CancelEventArgs> Starting;
        public event EventHandler<StepCompletedEventArgs> StepCompleted;
        public event EventHandler Completed;
    }

    public class StepCompletedEventArgs : CancelEventArgs
    {
        IWorkStep _step;

        public StepCompletedEventArgs(IWorkStep step): base()
        {
            _step = step;
        }

        public IWorkStep CompletedStep { get { return _step; } }
    }
}
